#region Copyright Notices
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Library General Public License for more details.
// 
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free
// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#endregion

using System;

namespace Yarr
{
	/// <summary>
	/// Provides an interface so that tracers, debuggers and profilers can hook into
    /// the evaluation process. If a Yarr.IProfiler is assigned to Runtime.Profiler, 
    /// then the runtime will call the various IProfiler methods as described below.
	/// </summary>
	public interface IProfiler
	{
        /// <summary>
        /// Invoked when the profiler is first assigned to the  Runtime.Profiler property.
        /// </summary>
        void Open();

        /// <summary>
        /// Invoked every time that eval is invoked. The arguments passsed to TraceCall 
        /// are the expression that is about to be evaluated, and the execution environment 
        /// where it will be evaluated.
        /// </summary>
        /// <param name="form">The Yarr expression for the eval call</param>
        /// <param name="env">The execution environment</param>
        /// <returns>The object returned by TraceCall becomes the form that is evaluated</returns>
        Object TraceCall (object form, Environment env);

        /// <summary>
        /// Invoked every time that eval returns. The arguments passed to TraceReturn 
        /// are the return value of eval, and the execution environment where the evaluation took place. 
        /// The TraceReturn function may return the resultant object, or may substitute another object. 
        /// The return value of TraceReturn becomes the return value of eval.
        /// </summary>
        /// <param name="form">The Yarr expression being returned</param>
        /// <param name="env">The curent execution environment</param>
        /// <returns>
        /// Will typically return <paramref name="form"/>, but can return any value, which will
        /// become the return value of the eval
        /// </returns>
		object TraceReturn (object form, Environment env);

        /// <summary>
        ///  Invoked to provide an informational message. The runtime does not call this method, 
        ///  but it can be called by custom code, or by Yarr code via (pr 'debug ...) or (prl 'debug ...)
        /// </summary>
        /// <param name="str">The comment</param>
        /// <param name="env">The curent execution environment</param>
        void Comment (string str, Environment env);

        /// <summary>
        /// Invoked whenever another IProfiler object is assigned to Runtime.Profiler (replacing the current one)
        /// </summary>
		void Close();
	}

    /// <summary>
    /// Supported debugger modes
    /// </summary>
    public enum DebuggerMode
    {
        /// <summary>
        /// Single-step
        /// </summary>
        SingleStep,

        /// <summary>
        /// Step over current form
        /// </summary>
        StepOver,

        /// <summary>
        /// Step out of frame
        /// </summary>
        RunToPreviousFrame,

        /// <summary>
        /// Run until breakpoint hit
        /// </summary>
        RunToBreakpoint,

        /// <summary>
        /// Run until completion
        /// </summary>
        RunToCompletion,

        /// <summary>
        /// Abort execution
        /// </summary>
        Abort
    }


    /// <summary>
    /// Debugger services
    /// </summary>
    public interface IDebugger
    {
        /// <summary>
        /// Add a breakpoint
        /// </summary>
        /// <param name="fn">Object to which to add breakpoint</param>
        void AddBreakpoint (Object fn);

        /// <summary>
        /// Remove a breakpoint
        /// </summary>
        /// <param name="fn">Object from which to remove breakpoint</param>
        void RemoveBreakpoint (Object fn);

        /// <summary>
        /// Enable existing breakpoint
        /// </summary>
        /// <param name="fn">Object whose breakpoint is to be enabled</param>
        void EnableBreakpoint (Object fn);

        /// <summary>
        /// Disable existing breakpoint
        /// </summary>
        /// <param name="fn">Object whose breakpoint is to be disabled</param>
        void DisableBreakpoint (Object fn);

        /// <summary>
        /// Determine if a breakpoint is enabled
        /// </summary>
        /// <param name="breakpoint">Object with breakpoint</param>
        /// <returns>true if a breakpoint is enabled on the object, false otherwise</returns>
        bool IsBreakpointEnabled (Object breakpoint);

        /// <summary>
        /// Gets all breakpoints
        /// </summary>
        /// <returns>An array of objects with breakpoints</returns>
        Object[] GetBreakpoints ();

        /// <summary>
        /// Gets the call stack on the current thread
        /// </summary>
        /// <returns>The Yarr call stack</returns>
        StackFrame[] GetCallstack ();

        /// <summary>
        /// Sets the debugger mode
        /// </summary>
        /// <param name="mode">The mode to set</param>
        /// <param name="frame">The effective stack frame</param>
        /// <param name="argument">An argument for the mode</param>
        void SetMode (DebuggerMode mode, StackFrame frame, int argument);
    }


    /// <summary>
    /// Debugger makes call-backs to these on break events
    /// </summary>
    public interface IDebuggerBreak
    {
        /// <summary>
        /// Called when a breakpoint is hit (either explicit or implicit)
        /// </summary>
        /// <param name="debugger">An IDebugger instance servicing this call</param>
        /// <param name="currentFrame">The current stack frame</param>
        /// <returns>The object returned replaces the form to be executed</returns>
        Object DebugBreak (IDebugger debugger, StackFrame currentFrame);

        /// <summary>
        /// Called for debug print messages
        /// </summary>
        /// <param name="debugger">An IDebugger instance servicing this call</param>
        /// <param name="comment">The debug message</param>
        /// <param name="env">The execution environment</param>
        void DebugPrint (IDebugger debugger, string comment, Environment env);

        /// <summary>
        /// Called when a stack frame is exited
        /// </summary>
        /// <param name="debugger">An IDebugger instance servicing this call</param>
        /// <param name="value">The value about to be returned</param>
        /// <param name="currentFrame">The stack frame being exited</param>
        /// <returns>The object returned replaces the value being returned</returns>
        Object ReturnValue (IDebugger debugger, StackFrame currentFrame, Object value);
    }
}
